Excepciones en Python: try/except/else/finally — cuándo usar cada cláusula

El bloque try/except de Python tiene cuatro cláusulas posibles, y cada una tiene un propósito diferente. Conocerlas bien evita la trampa más habitual: capturar excepciones demasiado amplias que silencian errores reales.

try/except básico

def dividir(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        print("Error: división por cero")
        return None

print(dividir(10, 2))   # 5.0
print(dividir(10, 0))   # Error: división por cero ? None

Capturar múltiples excepciones

def parsear_entero(texto):
    try:
        return int(texto)
    except (ValueError, TypeError) as e:
        # Tupla de tipos para capturar varios a la vez
        print(f"No se pudo parsear '{texto}': {e}")
        return None

print(parsear_entero("42"))    # 42
print(parsear_entero("abc"))   # No se pudo parsear 'abc': invalid literal...
print(parsear_entero(None))    # No se pudo parsear 'None': int() argument...

La cláusula else: solo si no hubo excepción

El bloque else se ejecuta solo si el bloque try terminó sin lanzar ninguna excepción. Separa el código de éxito del de manejo de errores:

import json

def cargar_config(ruta):
    try:
        with open(ruta) as f:
            datos = json.load(f)
    except FileNotFoundError:
        print(f"Fichero no encontrado: {ruta}")
        return {}
    except json.JSONDecodeError as e:
        print(f"JSON inválido en {ruta}: {e}")
        return {}
    else:
        # Solo llegamos aquí si open() y json.load() funcionaron
        print(f"Config cargada: {len(datos)} claves")
        return datos

La cláusula finally: siempre se ejecuta

finally se ejecuta siempre: haya excepción o no, haya return dentro del try o no. Es el lugar correcto para liberar recursos:

def leer_fichero(ruta):
    f = None
    try:
        f = open(ruta, encoding="utf-8")
        return f.read()
    except OSError as e:
        print(f"Error al leer: {e}")
        return ""
    finally:
        if f:
            f.close()
            print("Fichero cerrado")  # siempre

# En la práctica, usa 'with' que hace esto automáticamente
def leer_fichero_mejor(ruta):
    try:
        with open(ruta, encoding="utf-8") as f:
            return f.read()
    except OSError as e:
        print(f"Error al leer: {e}")
        return ""

Re-raise: propagar la excepción

import logging

def procesar_pago(importe):
    try:
        # simulación
        if importe > 10000:
            raise ValueError("Importe demasiado alto")
        return {"estado": "OK", "importe": importe}
    except ValueError as e:
        logging.error(f"Error de validación: {e}")
        raise   # re-lanza la excepción original con el traceback completo

try:
    procesar_pago(50000)
except ValueError as e:
    print(f"El pago falló: {e}")

Jerarquía de excepciones y los antipatrones

# MAL: captura demasiado amplia — oculta bugs
try:
    resultado = calcular_algo()
except Exception:   # o bare 'except:'
    pass            # silencia TODOS los errores, incluso bugs de código

# MAL: capturar y no hacer nada útil
try:
    conectar()
except Exception as e:
    print(e)  # sin logging, sin re-raise, sin manejo real

# BIEN: captura específica + acción útil
try:
    conectar()
except ConnectionRefusedError:
    logger.error("No se pudo conectar al servidor")
    raise SystemExit(1)

La regla: captura siempre el tipo de excepción más específico posible. Usa else para separar el camino feliz del manejo de errores. Usa finally para limpieza. Evita capturar Exception o usar except: desnudo salvo en el nivel más alto de tu programa.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP